/* 
 * File:   ConstanteParser.cpp
 * Author: Gab'z
 * 
 * Created on 5 mai 2012, 22:47
 */
#include <string.h>
#include <iostream>

#include "Constante.h"
#include "CalcParser.h"
#include "LogSystem.h"
#include "LogMessage.h"
#include "Entier.h"
#include "Rationnel.h"
#include "Reel.h"
#include "Complexe.h"
#include <math.h>
#include "CalcObjet.h"

CalcParser::~CalcParser() {
}

CalcObjet* CalcParser::parse(const std::string& str) const throw (ConstanteFormatException) {
    // si string vide ou contient espace => Echec
    if (str.empty() || str.find(" ") != str.npos) throw ConstanteFormatException(str);

    const char* c = str.c_str();
    const char min = '0';
    const char max = '9';
    if ((strcmp(c, &min) >= 1 || strcmp(c, &min) == 0) && (strcmp(c, &max) <= 1 || strcmp(c, &max) == 0)) {
        // if (('0' <= c*) && ('9' >= c*)) {
        // on tente d'en faire soit : entier, rationnel, reel, complexe
        if (isEntier(str)) {
            std::istringstream iss(str);
            // convertir string en un int puis Entier
            int nombre;
            iss >> nombre;
            return new Entier(nombre);
        }



        // Un rationnel ? *pos est la *position du caractère /
        int position = 0;
        int* pos = &position;
        if (isRationnel(str, pos)) {
            int num;
            int denom;
            std::istringstream iss(str.substr(0, *pos));
            iss >> num;
            std::istringstream iss2(str.substr(*pos + 1, str.length() - *pos));
            iss2 >> denom;
            Rationnel * p_ra = new Rationnel(num, denom);
            return p_ra;
        }





        // Un reel ? *pos est la *position du caractère .
        if (isReel(str, pos)) {
            int pEntiere;
            int pDecimale;
            std::istringstream iss(str.substr(0, *pos));
            iss >> pEntiere;
            std::istringstream iss2(str.substr(*pos + 1, str.length() - *pos));
            iss2 >> pDecimale;
            Reel * p_re = new Reel(pEntiere + (double) pDecimale / pow(10, str.length() - *pos - 1));
            return p_re;
        }



        // Un complexe ? *pos est la *position du caractère $
        if (isComplexe(str, pos)) {
            CalcObjet* p_c1 = parse(str.substr(0, *pos));
            CalcObjet* p_c2 = parse(str.substr(*pos + 1, str.length() - *pos));

            Entier* p_ns1e = 0;
            Entier* p_ns2e = 0;
            Rationnel* p_ns1ra = 0;
            Rationnel* p_ns2ra = 0;
            Reel* p_ns1re = 0;
            Reel* p_ns2re = 0;

            try {
                p_ns1e = dynamic_cast<Entier*> (p_c1);
                if (p_ns1e == 0) {
                    p_ns1ra = dynamic_cast<Rationnel*> (p_c1);
                    if (p_ns1ra == 0) {
                        p_ns1re = dynamic_cast<Reel*> (p_c1);
                    }
                }
                p_ns2e = dynamic_cast<Entier*> (p_c2);
                if (p_ns2e == 0) {
                    p_ns2ra = dynamic_cast<Rationnel*> (p_c2);
                    if (p_ns2ra == 0) {
                        p_ns2re = dynamic_cast<Reel*> (p_c2);
                    }
                }
            } catch (std::exception e) {
                std::cout << std::string(e.what());
                throw ConstanteFormatException(str);
            }


            Complexe* p_c = 0;
            if (p_ns1e && p_ns2e) p_c = new Complexe(p_ns1e, p_ns2e);
            else if (p_ns1e && p_ns2ra) p_c = new Complexe(p_ns1e, p_ns2ra);
            else if (p_ns1e && p_ns2re) p_c = new Complexe(p_ns1e, p_ns2re);
            else if (p_ns1ra && p_ns2e) p_c = new Complexe(p_ns1ra, p_ns2e);
            else if (p_ns1ra && p_ns2ra) p_c = new Complexe(p_ns1ra, p_ns2ra);
            else if (p_ns1ra && p_ns2re) p_c = new Complexe(p_ns1ra, p_ns2re);
            else if (p_ns1re && p_ns2e) p_c = new Complexe(p_ns1re, p_ns2e);
            else if (p_ns1re && p_ns2ra) p_c = new Complexe(p_ns1re, p_ns2ra);
            else if (p_ns1re && p_ns2re) p_c = new Complexe(p_ns1re, p_ns2re);
            else if (p_c == 0) {
                std::cout << "p_c == 0 : Aucune correspondance trouvée\n";
                throw ConstanteFormatException("Nombre mal écrit (apres couplage) : " + str);
            }
            delete p_ns1e;
            delete p_ns1ra;
            delete p_ns1re;
            delete p_ns2e;
            delete p_ns2ra;
            delete p_ns2re;
//                        delete p_c1;
//                        delete p_c2;
            //            Complexe* p_c = new Complexe(Reel(4.2),Rationnel(7/8));
            return p_c;
        }

        // si on arrive ici, la saisie commence par un chiffre mais n'a pas pu 
        // être traduite en une implémentation de Nombre
        LogSystem::log(LogMessage(LogSystem::ERROR, "Nombre mal écrit (apres complexe) : " + str));
        throw ConstanteFormatException("Nombre mal écrit (apres complexe) : " + str);
    }

    // si elle commence par ' ET TERMINE par '
    // on copie la chaine dans une expression pour évaluation ultérieure



    // on tente de trouver un opérateur
    if (isOperateur(str)) {
        Operateur* op = opMap->getOperateur(str);
        return op;
    }
    
    
    
    LogSystem::log(LogMessage(LogSystem::ERROR, "Aucune correspondance pour " + str));
    throw ConstanteFormatException("Aucune correspondace pour : " + str);

}